home *** CD-ROM | disk | FTP | other *** search
/ Chip 2006 June (Extra) / CHIP 2006-06.3.iso / program / opensource / clamav-devel.exe / contrib / Windows / clamserver.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2006-05-16  |  10.5 KB  |  446 lines

  1. /*
  2.  *  Copyright (C) 2004 Nigel Horne <njh@bandsman.co.uk>
  3.  *
  4.  *  This program is free software; you can redistribute it and/or modify
  5.  *  it under the terms of the GNU General Public License as published by
  6.  *  the Free Software Foundation; either version 2 of the License, or
  7.  *  (at your option) any later version.
  8.  *
  9.  *  This program is distributed in the hope that it will be useful,
  10.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  *  GNU General Public License for more details.
  13.  *
  14.  *  You should have received a copy of the GNU General Public License
  15.  *  along with this program; if not, write to the Free Software
  16.  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  17.  *  MA 02110-1301, USA.
  18.  */
  19. #include "stdafx.h"
  20.  
  21. #include "resource.h"
  22. #include "clamav.h"
  23.  
  24. #include "mainfrm.h"
  25.  
  26. #include "clamserver.h"
  27. #include "servername.h"
  28.  
  29. #include <io.h>
  30. #include <winsock.h>
  31. #include <sys/stat.h>
  32.  
  33. ClamServer::ClamServer(void)
  34. {
  35.     if(!InitInstance())
  36.         THROW(new CException());    // FIXME: never freed 
  37.  
  38.     progressBar = NULL;
  39.     stopping = FALSE;
  40. }
  41.  
  42. ClamServer::ClamServer(CString& serverName, unsigned short p)
  43. {
  44.     LPTSTR hostname = serverName.GetBuffer(64);    
  45.     serverIP = inet_addr(hostname);
  46.  
  47.     if(serverIP == -1L)
  48.         THROW(CException());
  49.  
  50.     port = p;
  51.  
  52.     const int sock = CreateConnection();
  53.  
  54.     if(sock < 0)
  55.         THROW(CException());
  56.  
  57.     progressBar = NULL;
  58.     stopping = FALSE;
  59. }
  60.  
  61. ClamServer::~ClamServer()
  62. {
  63.     if(progressBar) {
  64.         delete progressBar;
  65.         progressBar = NULL;
  66.     }
  67. }
  68.  
  69. BOOL
  70. ClamServer::InitInstance(void)
  71. {
  72.     ServerName serverName;
  73.     
  74.     if(serverName.DoModal() == IDCANCEL)
  75.         return FALSE;
  76.  
  77.     const char *hostname = serverName.m_serverName;
  78.  
  79.     serverIP = inet_addr(hostname);
  80.  
  81.     if(serverIP == -1L) {
  82.         AfxMessageBox("Unknown host");
  83.         return FALSE;
  84.     }
  85.  
  86.     port = (unsigned short)serverName.m_port;
  87.     const int sock = CreateConnection();
  88.  
  89.     if(sock < 0)
  90.         return TRUE;
  91.  
  92.     return CheckConnection(sock);
  93. }
  94.  
  95. const BOOL
  96. ClamServer::CheckConnection(int sock)
  97. {
  98.     if(send(sock, "PING\n", 5, 0) < 5) {
  99.         closesocket(sock);
  100.         AfxMessageBox("Can't talk to clamdserver");
  101.         return FALSE;
  102.     }
  103.     char ret[5];
  104.     if(recv(sock, ret, sizeof(ret), 0) <= 4) {
  105.         closesocket(sock);
  106.         AfxMessageBox("Can't receive from clamdserver");
  107.         return FALSE;
  108.     }
  109.     closesocket(sock);
  110.     if(strncmp(ret, "PONG\n", 5) != 0) {
  111.         AfxMessageBox("Is that server running clamd?");
  112.         return FALSE;
  113.     }
  114.  
  115.     return TRUE;    
  116. }
  117.  
  118. int
  119. ClamServer::CreateConnection(void)
  120. {
  121.     const int sock = socket(AF_INET, SOCK_STREAM, 0);
  122.     if(sock < 0) {
  123.         AfxMessageBox("Can't create socket");
  124.         return FALSE;
  125.     }
  126.  
  127.     struct sockaddr_in server;
  128.     memset(&server, '\0', sizeof(struct sockaddr_in));
  129.     server.sin_family = PF_INET;
  130.     server.sin_port = htons(port);
  131.     server.sin_addr.s_addr = serverIP;
  132.  
  133.     // TODO    display a message about connecting to the server. Include cancel button
  134.     if(connect(sock, (struct sockaddr *)&server, sizeof(struct sockaddr)) < 0) {
  135.         AfxMessageBox("Can't connect to clamdserver");
  136.         return FALSE;
  137.     }
  138.     return sock;
  139. }
  140.  
  141. // TODO: on recursive pop up box with progress bar - to include cancel button
  142. BOOL
  143. ClamServer::Scan(const CString& filename, int level, CMainFrame *mainFrame, CWnd *parent, BOOL recursive, const CString& qDir)
  144. {
  145.     if(level == 0)
  146.         stopping = FALSE;
  147.     else if(stopping) {
  148.         if(progressBar) {
  149.             delete progressBar;
  150.             progressBar = NULL;
  151.         }
  152.         // mainFrame->ChangeStatusText("");
  153.         return TRUE;
  154.     }
  155.  
  156.     // Don't scan folders "." and ".."
  157.      if(filename[filename.GetLength() - 1] == '.')
  158.          return TRUE;
  159.  
  160.     // I understand newer MFCs have 'PathIsDirectory'
  161.  
  162.     struct stat statb;
  163.  
  164.     if(stat(filename, &statb) < 0) {
  165.         // It could be that we've been given a wild card match
  166.  
  167.         WIN32_FIND_DATA findData;
  168.                 
  169.         HANDLE hFind = FindFirstFile(filename, &findData);
  170.  
  171.         if(hFind == INVALID_HANDLE_VALUE) {
  172.               // No we haven't...
  173.             AfxMessageBox(CString("Can't stat ") + filename);
  174.             return TRUE;
  175.         }
  176.         return this->ScanWildCard(filename, level, mainFrame, parent, recursive, qDir);
  177.     }
  178.  
  179.     if(progressBar && !stopping) {
  180.         if(progressBar->IsStopPressed())               
  181.             stopping = TRUE;
  182.         progressBar->SetFilename(filename);
  183.     }
  184.  
  185.     // mainFrame->ChangeStatusText(filename);    // statusBar.ShowProgress
  186.     // mainFrame->UpdateWindow();
  187.  
  188.     if(statb.st_mode&S_IFDIR) {
  189.         // Don't recurse unless we've been asked to
  190.  
  191.         if((!recursive) && (level > 0))
  192.             return TRUE;
  193.  
  194.         if(progressBar == NULL) {
  195.             // FIXME: not all return paths remove this, possible memory leak
  196.             progressBar = new CProgress(parent);
  197.             progressBar->Create(IDD_PROGRESS, parent);
  198.         }
  199.  
  200.         // Have been passed a folder.
  201.         return this->ScanFolder(filename, level, mainFrame, parent, recursive, qDir);
  202.  
  203.     }
  204.  
  205.     if(progressBar && (level == 0)) {
  206.         delete progressBar;
  207.         progressBar = NULL;
  208.     }
  209.  
  210.     const int commandSocket = CreateConnection();
  211.     
  212.     if(commandSocket < 0)
  213.         return TRUE;
  214.  
  215.     if(send(commandSocket, "STREAM\n", 7, 0) < 7) {
  216.         closesocket(commandSocket);
  217.         AfxMessageBox("Send failed to clamd");
  218.         return TRUE;
  219.     }
  220.  
  221.     char buf[64];
  222.     int nbytes = ClamdRecv(commandSocket, buf, sizeof(buf) - 1);
  223.  
  224.     if(nbytes < 0) {
  225.         closesocket(commandSocket);
  226.         AfxMessageBox("recv failed from clamd getting PORT");
  227.         return TRUE;
  228.     }
  229.     buf[nbytes] = '\0';
  230.  
  231.     unsigned short port;
  232.  
  233.     if(sscanf(buf, "PORT %hu\n", &port) != 1) {
  234.         closesocket(commandSocket);
  235.         AfxMessageBox("Didn't get PORT information from clamd");
  236.  
  237.         return TRUE;
  238.     }
  239.  
  240.     const int dataSocket = socket(AF_INET, SOCK_STREAM, 0);
  241.  
  242.     if(dataSocket < 0) {
  243.         closesocket(commandSocket);
  244.         AfxMessageBox("Can't create dataSocket");
  245.         return TRUE;
  246.     }
  247.  
  248.     shutdown(dataSocket, 0);
  249.  
  250.     struct sockaddr_in reply;
  251.     memset(&reply, '\0', sizeof(struct sockaddr_in));
  252.     reply.sin_family = PF_INET;
  253.      reply.sin_port = htons(port);
  254.     reply.sin_addr.s_addr = serverIP;
  255.  
  256.     const int rc = connect(dataSocket, (struct sockaddr *)&reply, sizeof(struct sockaddr_in));
  257.     if(rc < 0) {
  258.         closesocket(commandSocket);
  259.         closesocket(dataSocket);
  260.         AfxMessageBox("Failed to connect to port given by clamd");
  261.         return TRUE;
  262.     }
  263.  
  264.     CFile file;
  265.  
  266.     if(!file.Open(filename, CFile::modeRead|CFile::typeBinary|CFile::shareDenyNone)) {
  267.         closesocket(commandSocket);
  268.         closesocket(dataSocket);
  269.  
  270.         AfxMessageBox(CString("Can't open ") + filename + " to scan: ");
  271.         return TRUE;
  272.     }
  273.  
  274.     if(progressBar)
  275.         progressBar->SetPercent(0);
  276.  
  277.     char buffer[1500];    // TODO: send in MTU byte chunks
  278.     off_t bytesSent = (off_t)0;
  279.  
  280.     BOOL error = FALSE;
  281.  
  282.     while(((nbytes = file.Read(buffer, sizeof(buffer))) > 0) && !stopping) {
  283.         // Every block see if someone wants to do something
  284.         MSG Msg;
  285.  
  286.         if(::PeekMessage(&Msg, NULL, WM_NULL, WM_USER - 1, PM_NOREMOVE)) {
  287.                ::PeekMessage(&Msg, NULL, WM_NULL, WM_USER - 1, PM_REMOVE);
  288.                TranslateMessage(&Msg);
  289.                DispatchMessage(&Msg);
  290.  
  291.             if((progressBar && progressBar->IsStopPressed()) ||
  292.                (Msg.message == WM_QUIT)) {
  293.                 error = TRUE;
  294.                 break;
  295.             }
  296.         }
  297.  
  298.         char buf[81];
  299.         if(ClamdRecv(commandSocket, buf, sizeof(buf) - 1, 0) > 0) {
  300.             AfxMessageBox(buf);
  301.             error = TRUE;
  302.             break;
  303.         }
  304.             
  305.         if(send(dataSocket, buffer, nbytes, 0) != nbytes) {
  306.             AfxMessageBox("Send error to clamd");
  307.             error = TRUE;
  308.             break;
  309.         }
  310.  
  311.         if(progressBar) {
  312.             bytesSent += nbytes;
  313.  
  314.             progressBar->SetPercent((int)(bytesSent * 100 / statb.st_size)); 
  315.         }
  316.     }
  317.  
  318.     closesocket(dataSocket);
  319.     
  320.     file.Close();
  321.  
  322.     if(error) {
  323.         closesocket(commandSocket);
  324.         stopping = TRUE;
  325.         if(progressBar && (level == 0)) {
  326.             delete progressBar;
  327.             progressBar = NULL;
  328.         }    
  329.         return TRUE;
  330.     }
  331.  
  332.     nbytes = ClamdRecv(commandSocket, buffer, sizeof(buffer) - 1);
  333.  
  334.     closesocket(commandSocket);
  335.  
  336.     if(nbytes < 0) {
  337.         AfxMessageBox("recv error getting status");
  338.         return TRUE;
  339.     } else if(nbytes == 0)
  340.         return TRUE;
  341.  
  342.     buffer[nbytes] = '\0';
  343.  
  344.     if(strstr(buffer, "ERROR") != NULL) {
  345.         AfxMessageBox(filename + " " + buffer);
  346.         return TRUE;
  347.     }
  348.  
  349.     // TODO: if we're scanning down a directory tree
  350.     // don't display a popup box - update a dialog box
  351.     // which tells us how far we are
  352.     
  353.     if(strstr(buffer, "FOUND") == NULL)
  354.         return TRUE;
  355.     AfxMessageBox(filename + " " + buffer);
  356.  
  357.     mainFrame->ChangeStatusText(filename + " " + buffer);    // statusBar.ShowProgress
  358.  
  359.     return FALSE;
  360. }
  361.  
  362. BOOL
  363. ClamServer::ScanFolder(const CString& string, int level, CMainFrame *mainFrame, CWnd *parent, BOOL recursive, const CString& qDir)
  364. {
  365.     return ScanWildCard(string + "\\*.*", level, mainFrame, parent, recursive, qDir);
  366. }
  367.  
  368. BOOL
  369. ClamServer::ScanWildCard(const CString& string, int level, CMainFrame *mainFrame, CWnd *parent, BOOL recursive, const CString& qDir)
  370. {
  371.     if(stopping)
  372.         return TRUE;
  373.  
  374.     WIN32_FIND_DATA findData;
  375.         
  376.     HANDLE hFind = FindFirstFile(string, &findData);
  377.  
  378.     if(hFind == INVALID_HANDLE_VALUE)
  379.           // No files in this folder
  380.           return TRUE;
  381.  
  382.     // Get to the filename stub - i.e. the file without the trailing \*.*
  383.     const int index = string.Find("\\*.*");
  384.  
  385.     ASSERT(index >= 0);
  386.  
  387.     const CString stub = string.Left(index);
  388.              
  389.       BOOL rc = TRUE; 
  390.  
  391.      do
  392.           //if(findData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
  393.            // Recurse into this folder/file only if recurse enabled
  394.                // if(!this->Scan(filename + "\\" + findData.cFileName))
  395.                 // break out as soon as one virus is found
  396.                 // TODO: optionally report all found
  397.                 // return FALSE;
  398.         if(!this->Scan(stub + "\\" + findData.cFileName, level + 1, mainFrame, parent, recursive, qDir))
  399.             rc = FALSE;
  400.     
  401.       while(FindNextFile(hFind, &findData) && !stopping);
  402.  
  403.     if(progressBar && (level == 0)) {
  404.         delete progressBar;
  405.         progressBar = NULL;
  406.     }
  407.  
  408.     return rc;
  409. }
  410.  
  411. /*
  412.  * Read from clamav - timeout if necessary
  413.  * timeout defaults to 30 seconds, -1 = wait forever, 0 = poll
  414.  * TODO: default time should be read from clamav.conf
  415.  */
  416. int
  417. ClamServer::ClamdRecv(int sock, char *buf, size_t len, int timeout /* = 30 */)
  418. {
  419.     fd_set rfds;
  420.     struct timeval tv;
  421.  
  422.     if(timeout == -1)
  423.         return recv(sock, buf, len, 0);
  424.  
  425.     FD_ZERO(&rfds);
  426.     FD_SET(sock, &rfds);
  427.  
  428.     tv.tv_sec = timeout;       // TODO: from clamav.conf
  429.     tv.tv_usec = 0;
  430.  
  431.     switch(select(sock + 1, &rfds, NULL, NULL, &tv)) {
  432.         case -1:
  433.             AfxMessageBox("select failed");
  434.             return -1;
  435.         case 0:
  436.             if(timeout != 0)
  437.                 AfxMessageBox("Timeout waiting for data from clamd");
  438.              return 0;
  439.     }
  440.  
  441.     return recv(sock, buf, len, 0);
  442. }
  443.  
  444. // void __cdecl __interrupt __far intFhandler(void) {
  445. // }
  446.